//短期内看不到 可以直接用的包；react-router 6.0也不能搞定问题，矛盾问题大，需要定做=舍弃与选择。
//可参考D:\coding\Relay\typescript-relayjs-examples-master\relay-entrypoints\src\ServerEntry.ts
import { createBrowserHistory } from 'history';
import { matchRoutes } from 'react-router-config';

/**
 * A custom router built from the same primitives as react-router. Each object in `routes`
 * contains both a Component and a prepare() function that can preload data for the component.
 * The router watches for changes to the current location via the `history` package, maps the
 * location to the corresponding route entry, and then preloads the code and data for the route.
 * 根据路由表对象routes【】来生成context的。
 * context才有用的。
 */
export default function createRouter(routes, options) {
  // Initialize history
  const history = createBrowserHistory(options);

  // Find the initial match and prepare it
  const initialMatches = matchRoute(routes, history.location);
  const initialEntries = prepareMatches(initialMatches);
  let currentEntry = {
    location: history.location,
    entries: initialEntries,
  };

  // maintain a set of subscribers to the active entry
  let nextId = 0;
  const subscribers = new Map();

  // Listen for location changes, match to the route entry, prepare the entry,
  // and notify subscribers. Note that this pattern ensures that data-loading
  // occurs *outside* of - and *before* - rendering.
  /**注册 history监听路由变化的 机制
   * */
  const cleanup = history.listen((location, action) => {
    if (location.pathname === currentEntry.location.pathname) {
      return;
    }
    const matches = matchRoute(routes, location);
    //some() 方法会依次执行数组的每个元素：如果有一个元素满足条件，则表达式返回true , 剩余的元素不会再执行检测。
    //实际上只能返回一个路由的;
    const entries = prepareMatches(matches);
    const nextEntry = {
      location,
      entries,
    };
    currentEntry = nextEntry;
    //导致页面内容切换的入口是这里:
    //cb是路由提供？
    subscribers.forEach(cb => cb(nextEntry));
  });

  //传给<RoutingContext.Provider value= 没有类型定义？
  // The actual object that will be passed on the RoutingConext.
  //底下history等5个参数项目都是有用的。提供给<Link>使用预加载源代码。
  const context = {
    history,
    get() {
      return currentEntry;
    },
    preloadCode(pathname) {
      //根据URI来加载组件代码，但是不会主动获取数据。
      // preload just the code for a route, without storing the result
      const matches = matchRoutes(routes, pathname);
      //预加载的情况: 点击Link 链接或者 浏览器地址栏目输入URI进入的;
      //matches.forEach(({ route }) => route.component.load());
      matches.forEach(({ route }) => route.component.preload());
      //非预加载的模式:
      //matches.forEach(({ route }) => route.component);
    },
    preload(pathname) {
      // preload the code and data for a route, without storing the result
      //点击路由赋能的Link链接，会提前加载数据。
      const matches = matchRoutes(routes, pathname);
      prepareMatches(matches);
    },
    subscribe(cb) {
      const id = nextId++;
      const dispose = () => {
        subscribers.delete(id);
      };
      //导致页面内容切换的入口是这里：cb()回调函数去执行cb:
      subscribers.set(id, cb);
      return dispose;
    },
  };

  // Return both the context object and a cleanup function
  return { cleanup, context };
}

/**
 * Match the current location to the corresponding route entry.
 */
function matchRoute(routes, location) {
  const matchedRoutes = matchRoutes(routes, location.pathname);
  if (!Array.isArray(matchedRoutes) || matchedRoutes.length === 0) {
    throw new Error('No route for ' + location.pathname);
  }
  return matchedRoutes;
}

/**
 * Load the data for the matched route, given the params extracted from the route
 * 如果是静态的组件 也会延迟源程序代码加载但是无数据的；
 * 注意 route.component是组件源代码，并非组件本尊。
 * 实际上点击才触发的，浏览器地址URL输入触发。
 */
function prepareMatches(matches) {
  //每一个路由匹配到的组件都会加载数据。
  return matches.map(match => {
    const { route, match: matchData } = match;
    //真正发起服务端请求的。
    const prepared= route.prepare? route.prepare(matchData.params) : null;
    //.get()返回可为null
    //const Component = route.component.get();
    //eagerly load 加载源程序代码；
    //if(route.component.get() == null) {}
    if(route.component) {
      //route.component.load();    //触发加载源代码
      route.component.preload();
    }
    //prepared= 加载Relay数据的请求/数据提前获取。
    //这个地方不能指望直接能component.read到组件代码，否则promise抛出错误！！
    //var _resource$read = route.component.read();
    //只能是单一个文件名的缺省输出的！
    //const  Component = _resource$read["default"];
    //const Component = props.component.read();
   // console.log("prepareMatches跑来route.component=", route.component,"matchData=",matchData);
    //route.component实际上还不是组件的
    return { component: route.component, prepared, routeData: matchData };
  });
}

